package horde

import (
	"errors"
	"event-chain/core"
	"event-chain/core/tools"
	"fmt"
	"time"

	"github.com/duke-git/lancet/v2/slice"
	"github.com/samber/lo"
)

type IHordeChain[T any, K comparable] interface {
	Init(func() ([]T, error)) error
	Erase(K, OptType, int, bool, bool)
	RegisterAlter(func(T, int) T, func(T) K, K, bool, bool) int
	RegisterFilter(func(int, T) bool, K, bool, bool) int
	RegisterSorter(func(T, T) bool, K, bool, bool) int
	Process()
}

type OptType int

const (
	AlterOpt OptType = iota
	FilterOpt
	SorterOpt
)

var (
	PreOpts   = []OptType{FilterOpt, AlterOpt, SorterOpt}
	PostOpts  = []OptType{AlterOpt, SorterOpt, FilterOpt}
	logOptMap = map[OptType]string{AlterOpt: "alter", FilterOpt: "filter", SorterOpt: "sorter"}
)

type OptMap[T any, K comparable] struct {
	FilterMap map[K][]IFilter[T]
	SorterMap map[K][]ISorter[T]
	AlterMap  map[K][]IAlter[T, K]
}

type Chain[T any, K comparable] struct {
	Data       []T
	logger     tools.Logger
	preOptMap  OptMap[T, K]
	postOptMap OptMap[T, K]
	deepCopy   func(T) T
	tier       uint8
}

func NewChain[T any, K comparable](logger tools.Logger, tier uint8, dataCp func(T) T) *Chain[T, K] {
	return &Chain[T, K]{
		logger:   logger,
		deepCopy: dataCp,
		preOptMap: OptMap[T, K]{
			FilterMap: make(map[K][]IFilter[T]),
			SorterMap: make(map[K][]ISorter[T]),
			AlterMap:  make(map[K][]IAlter[T, K]),
		},
		postOptMap: OptMap[T, K]{
			FilterMap: make(map[K][]IFilter[T]),
			SorterMap: make(map[K][]ISorter[T]),
			AlterMap:  make(map[K][]IAlter[T, K]),
		},
		tier: tier,
	}
}

func (c *Chain[T, K]) Init(init func() ([]T, error)) error {
	if init == nil {
		return errors.New("init func is nil")
	}
	data, err := init()
	if err != nil {
		return err
	}
	if data == nil {
		err = fmt.Errorf("init data is nil")
		c.logger.Errorf(err.Error())
		return err
	}
	c.Data = data
	return nil
}

func (c *Chain[T, K]) Erase(key K, opt OptType, index int, isPre, trigger bool) {
	if isPre {
		c.sweep(c.preOptMap, key, index, opt)
	} else {
		c.sweep(c.postOptMap, key, index, opt)
	}
	if trigger {
		c.Process()
	}
}

func (c *Chain[T, K]) sweep(optMap OptMap[T, K], key K, index int, opt OptType) {
	switch opt {
	case AlterOpt:
		wipe(optMap.AlterMap, key, index)
	case FilterOpt:
		wipe(optMap.FilterMap, key, index)
	case SorterOpt:
		wipe(optMap.SorterMap, key, index)
	default:
		c.logger.Errorf("invalid opt type: %d", opt)
	}
}

func wipe[T any, K comparable](m map[K][]T, key K, index int) {
	switch index {
	case core.InValid:
		return
	case core.DelAll:
		delete(m, key)
	default:
		m[key] = slice.Filter(m[key], func(i int, _ T) bool { return i != index })
	}
}

func (c *Chain[T, K]) getRegisterMap(isPre bool) OptMap[T, K] {
	if isPre {
		return c.preOptMap
	}
	return c.postOptMap
}

func (c *Chain[T, K]) RegisterAlter(f func(T, int) T, getKey func(T) K, key K, isOnce, isPre bool) int {
	return c.registerWrapper(AlterOpt, f, getKey, isOnce, isPre, func(useOptMap OptMap[T, K]) int {
		useOptMap.AlterMap[key] = append(useOptMap.AlterMap[key], NewAlter(getKey, f, isOnce))
		return len(useOptMap.AlterMap[key]) - 1
	})
}

func (c *Chain[T, K]) RegisterFilter(f func(int, T) bool, key K, isOnce, isPre bool) int {
	return c.registerWrapper(FilterOpt, f, nil, isOnce, isPre, func(useOptMap OptMap[T, K]) int {
		useOptMap.FilterMap[key] = append(useOptMap.FilterMap[key], NewFilter(f, isOnce))
		return len(useOptMap.FilterMap[key]) - 1
	})
}

func (c *Chain[T, K]) RegisterSorter(f func(T, T) bool, key K, isOnce, isPre bool) int {
	return c.registerWrapper(SorterOpt, f, nil, isOnce, isPre, func(useOptMap OptMap[T, K]) int {
		useOptMap.SorterMap[key] = append(useOptMap.SorterMap[key], NewSorter(f, isOnce))
		return len(useOptMap.SorterMap[key]) - 1
	})
}

func (c *Chain[T, K]) Process() {
	start := time.Now()
	defer func(start time.Time) {
		end := time.Now()
		c.logger.Infof("process end:%s used time: %v", end.Format("2006-01-02 15:04:05.000"), time.Since(start))
	}(start)
	c.logger.Infof("process start:%s", start.Format("2006-01-02 15:04:05.000"))
	data := process(c.Data, c.preOptMap, PreOpts, c.logger)
	c.Data = process(data, c.postOptMap, PostOpts, c.logger)
}

func process[T any, K comparable](data []T, optMap OptMap[T, K], opts []OptType, logger tools.Logger) []T {
	defer func() {
		if err := recover(); err != nil {
			logger.Errorf("process err: %v", err)
		}
	}()
	for _, optType := range opts {
		logger.Debugf("Process, %s, %v\n", logOptMap[optType], data)
		switch optType {
		case AlterOpt:
			data = execAlter(data, optMap.AlterMap)
		case FilterOpt:
			data = execFilter(data, optMap.FilterMap)
		case SorterOpt:
			data = execSorter(data, optMap.SorterMap)
		default:
			logger.Errorf("invalid opt type: %d", optType)
		}
	}
	return data
}

func execAlter[T any, K comparable](data []T, alterMap map[K][]IAlter[T, K]) []T {
	return runWrapper(alterMap, data, func(handler IAlter[T, K]) ([]T, bool) {
		res := lo.Map(data, handler.Handle)
		return res, handler.Once()
	})
}

func execFilter[T any, K comparable](data []T, filterMap map[K][]IFilter[T]) []T {
	return runWrapper(filterMap, data, func(handler IFilter[T]) ([]T, bool) {
		res := slice.Filter(data, handler.Handle)
		return res, handler.Once()
	})
}

func execSorter[T any, K comparable](data []T, sorterMap map[K][]ISorter[T]) []T {
	return runWrapper(sorterMap, data, func(handler ISorter[T]) ([]T, bool) {
		slice.SortBy(data, handler.Handle)
		return data, handler.Once()
	})
}
